perm filename TCPBG.MAC[IP,SYS] blob sn#680218 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-TCP>TCPBG.MAC.40303 30-Mar-82 23:03:43, Edit by CLYNN
; Do not recompute TMXRT/TMNRT if using newer algorithm
;<403-TCP>TCPBG.MAC.40302 9-Mar-82 20:24:56, Edit by CLYNN
; Updated for TCP release 3
; Support TVTs with JCN -1;  Use permanently allocated TCBDQ
; queue head for DEADQ in SCAN, initialize it in BGINI
;ISIMON:<ISI.TCP>TCPBG.MAC.3400 18-Sep-81 16:19:36, Edit by CHASE
;#340 Don't release TCBs until tty data has its TCB pointer cleared out.
;[BBNF]<401-TCP>TCPBG.MAC.19, 10-Jul-81 11:30:00, Ed: CLYNN
; Remove call to INIT from BGINI

	SEARCH	INPAR,TCPPAR,PROLOG
	TTITLE	TCPBG
	SUBTTL	TCP Background Routines, William W. Plummer, 16FEB77
	SWAPCD

COMMENT	!

	Routines in this file run periodically at a low rate to
	check for things such as dead (closed) TCBs and connections
	which need to be poked in order that they resynchronized.


* BACKG ....  3 ...... Background
  SCAN .....  4 ...... Scan all TCB's
  FUNCS ....  6 ...... Apply functions to a TCB
  REMBFS ...  6 ...... Remove buffers from done queue
  UPDATE ...  7 ...... Update retransmission variables
  DEADP ....  8 ...... Flush dead TCBs
  SCAVNG ... 10 ...... Scavenge all non-essential storage
* BGINI .... 11 ...... Initialize RX process block

	!

IFNKA <IFNDEF %%REL4,<
	PRINTX % %%REL4 NOT SET IN PROLOG0.MAC.  ASSUMED TRUE.
	%%REL4==1
>>

; BACKG		Top level of background

;	CALL BACKG
;Ret+1:	Always

BACKG::	PUSH P,PKT
	PUSH P,TPKT
	MOVEI T1,TCBHLK		; Which lock to set
	XMOVEI T2,SCAN		; Function to run
	CALL LCKCAL		; Lock the lock and run the function
	SKIPE INTSVR		; Was a scavenge requested?
	  AOS INTSVC		; Yes.  Count it as having been done
	SETZM INTSVR		; Cancel scavenge request
	MOVX T1,PT%XX0		; Indicate no packet
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKT		; Yes, Flush out the packet printer buffer
	MOVE T1,TCPBGT		; Run again in a few seconds
	ADD T1,TODCLK
	MOVEI T2,BG		; Pointer to Background process block
	MOVEM T1,PRCWAK(T2)	; BG has no input queue.
IFNKA <IFN %%REL4,<NOSKD1>>
	CALL TVTCSO		; Force scan of TVT for output
	POP P,TPKT		; (& OKSKD1 if IFNKA & IFN %%REL4)
	POP P,PKT
	RET

; SCAN		Scan through all TCBs and apply the functions to each

;TCBH/	Locked		NOINT
;
;	CALL SCAN
;Ret+1:	Always

SCAN:	LOCAL <TCBHX,NXTTCB,Q,DEADQ>
	PUSH P,TCB

	MOVE T1,TCBDQ		; Locate queue head
	MOVEM T1,DEADQ		; Save pointer to the queue

	MOVEI T1,TCBHUC		; Pointer to TCBH Use Count
	SKIPE TCBHUC		; Is it 0 as required?
	  CALL DISE		; No.  Wait for that.

	MOVSI TCBHX,-TCBHSZ	; Set to loop through all of TCBH
SCAN1:	HRRZ Q,TCBHX		; Index into TCBH table
	ADD Q,TCBH		; Plus (ext) pointer to table base
	LOAD NXTTCB,QNEXT,(Q)	; Pointer to first thing on this queue
	SETSEC NXTTCB,INTSEC	; Make extended address
SCAN2:	MOVE TCB,NXTTCB		; Set current TCB
	CAMN TCB,Q		; Back to head of queue?
	  JRST SCAN3		; Yes.  On to next queue

	LOAD NXTTCB,QNEXT,(TCB)	; Setup for next time
	SETSEC NXTTCB,INTSEC	; Make extended address
	XMOVEI T1,TCBLCK(TCB)	; Pointer to lock
	XMOVEI T2,FUNCS		; Routine to apply functions
	MOVE T3,DEADQ		; Arg for DEADP
	CALL LCKCAL		; Lock the TCB and do the functions
	JRST SCAN2		; Go to next TCB on this queue
SCAN3:	AOBJN TCBHX,SCAN1	; On to next queue

	MOVE Q,DEADQ		; Queue of dead TCBs
	LOAD NXTTCB,QNEXT,(Q)	; First thing on the queue
	SETSEC NXTTCB,INTSEC	; Make extended address
SCAN4:	MOVE TCB,NXTTCB		; Advance down the queue
	LOAD NXTTCB,QNEXT,(TCB)	; Set for next time
	SETSEC NXTTCB,INTSEC	; Make extended address
	CAMN TCB,Q		; Back to head?
	  JRST SCANXX		; Yes.  Done.

	CALL REMBFS		; Remove all buffers from TCPDBQ

	LOAD T1,TOPNF,(TCB)	; Get open/close wait bit index
	CALL RELWTB		; Release it

	LOAD T1,TERRF,(TCB)	; Get error wait flag number
	CALL RELWTB		; Release it

	XMOVEI T1,TCBLCK(TCB)	; Pointer to the lock
	CALL RELLCK		; Delete that

	MOVE T1,TCB
	CALL DQ			; Dequeue from dead queue
	CALL RETBLK		; Return the storage
	SOS TCBCNT		; Now one less connection
	JRST SCAN4

SCANXX:	POP P,TCB
	RESTORE
	RET

; FUNCS		Apply a list of functions to a TCB

;TCB/	(Extended) Locked connection block
;T1/	(Extended) Pointer to queue head for dead TCBs
;TCBHLK/Locked		NOINT
;
;	CALL FUNCS
;Ret+1:	Always

FUNCS:	PUSH P,T1		; Arg is pointer to dead queue head
	SKIPE INTSVR		; Scavenge free storage requested?
	  CALL SCAVNG		; Yes.  Do it to this TCB.
	CALL UPDATE		; Update Retransmission variables
	POP P,T1		; Get back arg for DEADP
	CALLRET DEADP		; Check for completely closed



; REMBFS(TCB, TCPBDQ)		Remove buffers owned by dead TCB

;TCB/	(Extended) Pointer to dead TCB
;
;
;	CALL REMBFS
;Ret+1:	Always

REMBFS:	LOCAL <NEXT>
	PUSH P,BFR
	MOVE BFR,TCPBDQ		; Get pointer to queue head
	LOAD NEXT,QNEXT,(BFR)	; Get first thing on queue
REMBF1:	SETSEC NEXT,INTSEC	; Make extended address
	MOVE BFR,NEXT
	CAMN BFR,TCPBDQ		; Points at head...
	  JRST REMBFX		; means we are done
	LOAD NEXT,QNEXT,(BFR)	; Get next item on queue
	LOAD T1,BTCB,(BFR)	; Get owning TCB
	SETSEC T1,INTSEC	; Make extended address
	CAME T1,TCB		; Is it the one under consideration?
	  JRST REMBF1		; No.  Try next.
	MOVE T1,BFR		; What to dequeue
	CALL DQ			; Do it.
	CALL RETBLK		; And return the storage
	JRST REMBF1		; Scan some more

REMBFX:	POP P,BFR
	RESTORE
	RET

; UPDATE		Update retransmitter control variables

;TCB/	(Extended) Locked Connection Block
;TCBHLK/Locked		NOINT
;
;	CALL UPDATE
;Ret+1:	Always

UPDATE:	JE <TRXPN,TRXPD>,(TCB),UPDATX ; Omit if newer algorithm
	LOAD T1,TMXRT,(TCB)	; Maximum round trip time (millisec.)
	LOAD T2,TMNRT,(TCB)	; Minimum
	MOVE T3,T1
	SUB T3,T2		; Delta
	IDIVI T3,2		; Decay 50% towards min.
	SUB T1,T3
	STOR T1,TMXRT,(TCB)	; Set new max.
	IDIVI T3,5
	ADD T2,T3		; Expand by 10% towards max.
	STOR T2,TMNRT,(TCB)	; Set new min.
	ADD T1,T2		; 2 times new average
	ADDI T1,↑D1000		; Arrange for a half second above average
	ASH T1,-1
	STOR T1,TRXI,(TCB)	; Is the new retransmit interval
UPDATX:	RET

; DEADP		Collect dead (closed) TCBs on a queue

;T1/	(Extended) Pointer to head of DEAD Queue
;TCB/	(Extended) Locked connection block to be examined
;TCBHLK/Locked		NOINT
;
;	CALL DEADP
;Ret+1:	Always. TCB placed on dead queue if appropriate

DEADP:	LOCAL <DEADQ>
	MOVEM T1,DEADQ
	JN TSUOP,(TCB),DEADPX	; Keep if no CLOSE from user yet


; TVT processing
	JE TTVT,(TCB),DEADP1	; Jump if not a TVT
	LOAD T2,TVTL,(TCB)	;#340 Get tvt line number, if any
	JUMPE T2,DEADP2		;#340 Jump if no line number
	NOSKD1			;#340 Don't let tty data change under us
	CALL STADYN		;#340 Get tty datablock, if any
	 TDZA T1,T1		;#340 Skip load if line not active
	  LOAD T1,PTVT,(T2)	;#340 Get tcb pointer, if any
	OKSKD1			;#340
	JUMPN T1,DEADPX		;#340 Keep if tty datablock points to it
DEADP2:
	JN TERR,(TCB),DEADP0	; Jump to release JCN if error
	LOAD T1,TRSYN,(TCB)	; Receive state
	LOAD T2,TSSYN,(TCB)	; Send state
	CAIN T1,NOTSYN		; No error & FINs exchanged
	 CAIE T2,NOTSYN
	  JRST DEADP1		; Jump if not dead state (closed)
DEADP0:	LOAD T1,TJCN,(TCB)	; Get the JCN (owned by INTFRK)
	JUMPE T1,DEADP1		; Jump if USREVT already released it
	CALL RETJCN
	SETZRO TJCN,(TCB)	; (RETJCN can't do this if JCN = -1)
DEADP1:
; Common processing

	JN TJCN,(TCB),DEADPX	; Keep if user can still reference this
	LOAD T1,TSSYN,(TCB)	; Send state
	LOAD T2,TRSYN,(TCB)	; Recv state
	CAIN T1,NOTSYN
	CAIE T2,NOTSYN
	  JRST DEADPX		; Keep unless FINd on both sides

	LOAD T1,QNEXT,<+TCBRXQ(TCB)>
	CAIE T1,TCBRXQ(TCB)	; Compare as if in sec. 0
	  JRST DEADPX		; Keep if retransmit queue non-empty
	LOAD T1,QNEXT,<+TCBSBQ(TCB)>
	CAIE T1,TCBSBQ(TCB)
	  JRST DEADPX		; Keep if stuff waiting to be sent
	JN TSCB,(TCB),DEADPX	; Which might be a current SEND buffer

	LOAD T1,QNEXT,<+TCBRPQ(TCB)>
	CAIE T1,TCBRPQ(TCB)
	  JRST DEADPX		; Keep if packets waiting for RA
	LOAD T1,QNEXT,<+TCBRBQ(TCB)>
	CAIE T1,TCBRBQ(TCB)
	  JRST DEADPX		; Keep if RECV buffers waiting
	JN TRCB,(TCB),DEADPX	; or if there is a current RECV buffer

	SKIPN TCBQRA(TCB)	; Must not be queued for RA
	SKIPE TCBQPZ(TCB)	; Or PZ
	  JRST DEADPX
	SKIPN TCBQRX(TCB)	; Or RX
	SKIPE TCBQDG(TCB)	; Or DG
	  JRST DEADPX

	MOVE T1,TCB
	CALL DQ			; Dequeue the buffer from the TCBH queue
	MOVE T2,DEADQ
	CALL NQ			; And put on the Dead queue

DEADPX:	RESTORE
	RET

; Scavenge free storage from connection blocks.

; All packets on the reassembly queue are released to permit the TCP
; to continue functioning.  Retransmissions will replace them.

;TCB/	(Extended) Locked Connection block
;TCBHLK/Locked		NOINT
;
;	CALL SCAVNG
;Ret+1:	Always

SCAVNG:	PUSH P,PKT		; Save so it can be clobbered
SCAVN1:	LOAD PKT,QNEXT,<+TCBRPQ(TCB)>	; Get first thing on RA queue
	CAIN PKT,TCBRPQ(TCB)	; Empty if that is the head itself
	  JRST SCAVNX		; No more to get from this connection
	SETSEC PKT,INTSEC	; Make extended address
	MOVE T1,PKT		; What to dequeue
	CALL DQ			; Take it off the receive packet queue
	CALL RETPKT
	JRST SCAVN1

SCAVNX:	POP P,PKT
	RET

; BGINI			Initialize BG process block

;	CALL BGINI
;Ret+1:	ALways

BGINI::	LOCAL <PRC>
	MOVEI PRC,BG		; Pointer to the Process block for BG

	SETZM PRCQ(PRC)		; Input queue

	MOVEI T1,QSZ		; Size of a queue head
	CALL GETBLK		; Head must in same section as items
	JUMPE T1,BGINIX		; Give up, T1 is zero
	CALL INITQ		; Initialize it (not used)
	MOVEM T1,TCBDQ		; Queue head for SCAN

	XMOVEI T1,PRCLCK(PRC)	; Lock
	CALL CLRLCK		; Initilize it

	XMOVEI T1,BACKG		; Background function
	MOVEM T1,PRCROU(PRC)	; Routine address

	MOVE T1,TODCLK		; Now
	ADD T1,TCPBGT		; Plus a few seconds
	MOVEM T1,PRCWAK(PRC)	; Start BG for the first time

	SETZM PRCWOF(PRC)	; Store in process block

	MOVEI T1,BGRNCT		; Pointer to run counter via section 0
	MOVEM T1,PRCRNC(PRC)	; Put in standard place

	MOVEI T1,BGUSE		; Pointer to CPU use meter
	MOVEM T1,PRCTMR(PRC)	; Put in standard place

	MOVX T1,QSZ		; Size of a queue head
	CALL GETBLK		; Get that amount of free storage
	  JUMPE T1,BGINIX	; Lose
	MOVEM T1,TCBDQ		; Used at cleanup (don't want to fail then)
	CALL INITQ		; Initialize it

	HRROI T1,-1
BGINIX:
	RESTORE
	RET

	TNXEND
	END